/*
    Copyright 2006 Andrew Wilkinson <andrew@indiegigs.co.uk>

    This file is part of GL2x.

    GL2x is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    GL2x is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GL2x; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdlib.h>
#include <stdio.h>

#include "driver.h"
#include "displaylists/displaylists.h"

//define USE_IF

void GL2x_setDefaultState() {
    int i;

    viewport_x = Fixed_zero;
    viewport_y = Fixed_zero;
    viewport_half_height = Fixed_createi(120);
    viewport_half_width = Fixed_createi(160);

    cull_face = GL_BACK;

    alpha_func = GL_ALWAYS;
    alpha_ref = 0.0;

    polygon_front_mode = GL_FILL;
    polygon_back_mode = GL_FILL;

    shade_model = GL_SMOOTH;

    blend_func_sfactor = GL_ONE;
    blend_func_dfactor = GL_ZERO;

    depth_range_near = Fixed_zero;
    depth_range_far = Fixed_one;

    depth_mask = 1;

    depth_func = GL_LESS;

    point_size = Fixed_one;
    line_width = Fixed_one;

    bound_tex_1d = 0;
    bound_tex_2d = 0;
    bound_tex_3d = 0;

    GL2x_z_buffer = malloc(sizeof(Fixed)*320*240);
    Fixed* zbuf = GL2x_z_buffer;
    for(i=0; i < 320*240; i++) {
        *zbuf = Fixed_one;
        zbuf++;
    }

    current_shader = &GL2x_flatShade;

    tri_flags.x = 1; tri_flags.z = 1; tri_flags.colour = 0;

    GL2x_initTexCache(); /* set up texture cache */
    GL2x_initDisplayLists();
}

void GL2x_driver_message(enum message msg, unsigned long data) {
#ifdef HOST_GP2x
static int cmd = 2;
    int i, a, b;
    for(i = 0; i < 200; i++) {
        a = msg;
        b = 0;
        while(b < 32) {
            if((a & 1) == 1) {
                GP2x_putPixel(10 + 32-b, cmd*10, 0, 255, 0, 0);
            } else {
                GP2x_putPixel(10 + 32-b, cmd*10, 255, 0, 0, 0);
            }
            b++;
            a = a >> 1;
        }
        GP2x_flip();
    }
    cmd++;
#endif
#ifndef USE_IF
    switch(msg) {
    case DRAW_POLYGON:
#else
    if(msg == DRAW_POLYGON) {
#endif
        {
#ifdef HOST_GP2x
        for(i = 0; i < 200; i++) {
            GP2x_putPixel(100, 30, 0, 255, 255, 255);
        }
#endif
        struct glpoly* poly = (struct glpoly*)GP2x_convert_ptr_from(data);

        GL2x_render(poly);

        free(GP2x_convert_ptr_from(poly->vertices));
        free(poly);
        }
#ifdef USE_IF
    } else if(msg == CLEAR_SCREEN) {
#else
        break;
    case CLEAR_SCREEN:
#endif
        {
        int x, y;
        unsigned char r, g, b;
        Fixed* colour = (Fixed*)GP2x_convert_ptr_from(data);

#ifdef HOST_GP2x
        for(i = 0; i < 200; i++) {
            GP2x_putPixel(100, 40, 0, 255, 255, 255);
        }
#endif

        r = (unsigned char)Fixed_round(Fixed_mul(colour[0], Fixed_createi(255)));
        g = (unsigned char)Fixed_round(Fixed_mul(colour[1], Fixed_createi(255)));
        b = (unsigned char)Fixed_round(Fixed_mul(colour[2], Fixed_createi(255)));

        int c = GP2x_getColour15(r, g, b, 255);

#ifdef HOST_GP2x
        for(i = 0; i < 200; i++) {
            GP2x_putPixel(100, 10, 0, 255, 255, 255);
        }
#endif

        for(y = 239; y >= 0; y--) {
            for(x = 319; x >= 0; x--) {
                GP2x_putColour(x, y, c);
            }
        }
#ifdef HOST_GP2x
        cmd = 2;
#endif
        free(colour);
        }
#ifdef USE_IF
    } else if(msg == CLEAR_DEPTH) {
#else
        break;
    case CLEAR_DEPTH:
#endif
        {
#ifdef HOST_GP2x
        for(i = 0; i < 200; i++) {
            GP2x_putPixel(100, 50, 0, 255, 255, 255);
        }
#endif
        int i;
        Fixed* ptr = GL2x_z_buffer;
        for(i=320*240; i > 0; i--, ptr++) {
            *ptr = Fixed_one;
        }
        }
#ifdef USE_IF
    } else if(msg == SET_VIEWPORT) {
#else
        break;
    case SET_VIEWPORT:
#endif
        {
        int* vals = (int*)GP2x_convert_ptr_from(data);
        viewport_x = Fixed_createi(vals[0]);
        viewport_y = Fixed_createi(vals[1]);
        viewport_half_height = Fixed_createi(vals[2]);
        viewport_half_width = Fixed_createi(vals[3]);
        free(vals);
        }
#ifdef USE_IF
    } else if(msg == SET_CULL_FACE) {
#else
        break;
    case SET_CULL_FACE:
#endif
        {
        cull_face = data;
        }
#ifdef USE_IF
    } else if(msg == SET_ALPHA_FUNC) {
#else
        break;
    case SET_ALPHA_FUNC:
#endif
        {
        alpha_func = *((GLenum*)data);
        alpha_ref = *((GLclampf*)(data+sizeof(GLenum)));
        free((void*)data);
        }
#ifdef USE_IF
    } else if(msg == SET_POLYGON_MODE) {
#else
        break;
    case SET_POLYGON_MODE:
#endif
        {
        GLenum* vals = (GLenum*)data;
        if(vals[0] == GL_FRONT || vals[0] == GL_FRONT_AND_BACK) {
            polygon_front_mode = vals[1];
        }
        if(vals[0] == GL_BACK || vals[0] == GL_FRONT_AND_BACK) {
            polygon_back_mode = vals[1];
        }
        free((void*)data);
        }
#ifdef USE_IF
    } else if(msg == SET_BLEND_FUNC) {
#else
        break;
    case SET_BLEND_FUNC:
#endif
        {
        GLenum* vals = (GLenum*)data;
        blend_func_sfactor = vals[0];
        blend_func_dfactor = vals[1];
        free((void*)data);
        }
#ifdef USE_IF
    } else if(msg == SET_DEPTH_RANGE) {
#else
        break;
    case SET_DEPTH_RANGE:
#endif
        {
        Fixed* vals = (Fixed*)data;
        depth_range_near = vals[0];
        depth_range_far = vals[1];
        free((void*)data);
        }
#ifdef USE_IF
    } else if(msg == SET_DEPTH_MASK) {
#else
        break;
    case SET_DEPTH_MASK:
#endif
        {
        depth_mask = (GLboolean)data;
        }
#ifdef USE_IF
    } else if(msg == SET_DEPTH_FUNC) {
#else
        break;
    case SET_DEPTH_FUNC:
#endif
        {
        depth_func = (GLenum)data;
        }
#ifdef USE_IF
    } else if(msg == SET_SHADE_MODEL) {
#else
        break;
    case SET_SHADE_MODEL:
#endif
        {
        switch((GLenum)data) {
        case GL_FLAT:
            current_shader = &GL2x_flatShade;
            tri_flags.colour = 0;
            break;
        case GL_SMOOTH:
            current_shader = &GL2x_smoothShade;
            tri_flags.colour = 1;
            break;
        }
        }
#ifdef USE_IF
    } else if(msg == SET_DRAW_BUFFER) {
#else
        break;
    case SET_DRAW_BUFFER:
#endif
        {
        draw_buffer = (GLenum)data;
        }
#ifdef USE_IF
    } else if(msg == SET_READ_BUFFER) {
#else
        break;
    case SET_READ_BUFFER:
#endif
        {
        read_buffer = (GLenum)data;
        }
#ifdef USE_IF
    } else if(msg == SET_POINT_SIZE) {
#else
        break;
    case SET_POINT_SIZE:
#endif
        {
        point_size = (Fixed){ data };
        }
#ifdef USE_IF
    } else if(msg == SET_LINE_WIDTH) {
#else
        break;
    case SET_LINE_WIDTH:
#endif
        {
        line_width = (Fixed){ data };
        }
#ifdef USE_IF
    } else if(msg == BIND_TEXTURE_1D) {
#else
        break;
    case BIND_TEXTURE_1D:
#endif
        {
        bound_tex_1d = (GLuint)data;
        }
#ifdef USE_IF
    } else if(msg == BIND_TEXTURE_2D) {
#else
        break;
    case BIND_TEXTURE_2D:
#endif
        {
        bound_tex_2d = (GLuint)data;
        }
#ifdef USE_IF
    } else if(msg == BIND_TEXTURE_3D) {
#else
        break;
    case BIND_TEXTURE_3D:
#endif
        {
        bound_tex_2d = (GLuint)data;
        }
#ifdef USE_IF
    } else if(msg == SET_TEXTURE) {
#else
        break;
    case SET_TEXTURE:
#endif
        {
        GL2x_setTexture((struct gltexture*)data);
        }
#ifdef USE_IF
    } else if(msg == GL_ENABLE) {
#else
        break;
    case GL_ENABLE:
#endif
        {
#ifdef HOST_GP2x
        for(i = 0; i < 200; i++) {
            GP2x_putPixel(100, 20, 0, 255, 255, 255);
        }
#endif
#define enable(x, y) \
    case x: \
        y = 1; \
        break;

        switch((GLenum)data) {
        enable(GL_ALPHA_TEST, gl_alpha_test)
        enable(GL_DEPTH_TEST, gl_depth_test)
        enable(GL_TEXTURE_1D, gl_texture_1d)
        enable(GL_TEXTURE_2D, gl_texture_2d)
        }
#undef enable
        }
#ifdef USE_IF
    } else if(msg == GL_DISABLE) {
#else
        break;
    case GL_DISABLE:
#endif
        {
#ifdef HOST_GP2x
        for(i = 0; i < 200; i++) {
            GP2x_putPixel(100, 60, 0, 255, 255, 255);
        }
#endif
#define disable(x, y) \
    case x: \
        y = 0; \
        break;

        switch((GLenum)data) {
        disable(GL_ALPHA_TEST, gl_alpha_test)
        disable(GL_DEPTH_TEST, gl_depth_test)
        disable(GL_TEXTURE_1D, gl_texture_1d)
        disable(GL_TEXTURE_2D, gl_texture_2d)
        }
#undef disable
        }
#ifndef USE_IF
        break;
#endif
    case GEN_LISTS:
        GP2x_return(GL2x_genDisplayLists(data));
        break;
    case NEW_LIST:
        {
        GL2_newList((data >> 8) & 255, data & 255);
        break;
        }
    }
    /*
    //default:
        //printf("Unknown driver message: %i.\n", msg);
    }*/
#ifdef HOST_GP2x
    for(i = 0; i < 100; i++) {
        a = msg;
        b = 0;
        while(b < 32) {
            if((a & 1) == 1) {
                GP2x_putPixel(10 + 32-b, cmd*10-9, 0, 255, 0, 0);
            } else {
                GP2x_putPixel(10 + 32-b, cmd*10-9, 255, 0, 0, 0);
            }
            b++;
            a = a >> 1;
        }
        GP2x_flip();
    }
#endif
}

int GL2x_main() {
    unsigned long message;
    unsigned long value;

    GL2x_setDefaultState();

    while(1) {
        GP2x_wait_message(&message, &value);

        if(GL2x_use_display_list) {
            GL2x_addDisplayListMsg((enum message)message, value);
        } else {
            GL2x_driver_message((enum message)message, value);
        }
    }

    return 0;
}
